// 定义事件类型
export type EventType = string | symbol;

// 定义监听器类型
export type Listener<T> = (data: T) => void;

// 定义发布订阅错误
export class PubSubError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'PubSubError';
  }
}

export class PubSub<T> {
  private listeners: Map<EventType, Set<Listener<T>>>;
  private readonly maxListeners: number;

  constructor(maxListeners = 10) {
    this.listeners = new Map();
    this.maxListeners = maxListeners;
  }

  subscribe(event: EventType, listener: Listener<T>): () => void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }

    const listeners = this.listeners.get(event)!;

    // 检查监听器数量是否超过限制
    if (listeners.size >= this.maxListeners) {
      throw new PubSubError(
        `Max listeners (${this.maxListeners}) exceeded for event ${String(event)}`,
      );
    }

    listeners.add(listener);

    // 返回取消订阅的函数
    return () => {
      this.unsubscribe(event, listener);
    };
  }

  unsubscribe(event: EventType, listener: Listener<T>): boolean {
    const listeners = this.listeners.get(event);
    if (!listeners) {
      return false;
    }

    const result = listeners.delete(listener);
    if (listeners.size === 0) {
      this.listeners.delete(event);
    }

    return result;
  }

  publish(event: EventType, data: T): boolean {
    const listeners = this.listeners.get(event);
    if (!listeners || listeners.size === 0) {
      return false;
    }

    try {
      listeners.forEach(listener => {
        try {
          listener(data);
        } catch (error) {
          console.error(`Error in listener for event ${String(event)}:`, error);
        }
      });
      return true;
    } catch (error) {
      console.error(`Error publishing event ${String(event)}:`, error);
      return false;
    }
  }

  clearAllListeners(): void {
    this.listeners.clear();
  }

  clearEvent(event: EventType): boolean {
    return this.listeners.delete(event);
  }

  getListenerCount(event: EventType): number {
    return this.listeners.get(event)?.size || 0;
  }

  hasListeners(event: EventType): boolean {
    return this.getListenerCount(event) > 0;
  }

  getEvents(): EventType[] {
    return Array.from(this.listeners.keys());
  }
}
